/*
 * Copyright 2003 Program of Computer Graphics, Cornell University
 *     580 Rhodes Hall
 *     Cornell University
 *     Ithaca NY 14853
 * Web: http://www.graphics.cornell.edu/
 * 
 * Not for commercial use. Do not redistribute without permission.
 */
#include "stdafx.h"

#include "Ray.h"
#include "KDTree.h"
#include <float.h>
#include <cstdio>

#define MIN_DIR_COMPONENT 1e-9


void RayExt::initializeFromRay(const Ray &ray)
{
  origin = ray.origin;
  direction = ray.direction;
  tStart = ray.tStart;
  tEnd = ray.tEnd;
  //should we force normalization of ray directions???
  //force each direction component to have at least some minimum absolute value
  //so that we don't need to deal with corner case where a component is exactly zero
  //for normalized vectors, change is near the limits of float accuracy and should not 
  //alter the results significantly
  F32vec4 signBitMask = F32vec4(-0.0f);           //mask with just sign bits set
  F32vec4 v_minComp = F32vec4(MIN_DIR_COMPONENT); 
  //vector of minimum direction components with same sign as exact direction components
  F32vec4 minDir = (direction&signBitMask)|v_minComp; 
  //make a mask with ones when components are too near zero (when |direction| < MIN_DIR_COMPONENT)
  F32vec4 nearZeroMask = cmplt(_mm_andnot_ps(signBitMask,direction),v_minComp);
  //now select either exact or minimum direction component as specified by nearZeroMask
  direction = Select(minDir,direction,nearZeroMask);
//  direction = (nearZeroMask & minDir) | _mm_andnot_ps(nearZeroMask,direction);
  /*
  if (absv(dirX()) < MIN_DIR_COMPONENT) {
    dirX() = (dirX()<0)?(-MIN_DIR_COMPONENT):(MIN_DIR_COMPONENT);
  }
  if (absv(dirY()) < MIN_DIR_COMPONENT) {
    dirY() = (dirY()<0)?(-MIN_DIR_COMPONENT):(MIN_DIR_COMPONENT);
  }
  if (absv(dirZ()) < MIN_DIR_COMPONENT) {
    dirZ() = (dirZ()<0)?(-MIN_DIR_COMPONENT):(MIN_DIR_COMPONENT);
  }
  */
  const Is32vec4 mask3Int(0,-1,-1,-1);
  const F32vec4 mask3 = _mm_castsi128_ps(mask3Int);
  //create inverse directions
  invDirection = (F32vec4(1.0f)/direction)&mask3;
  //create replicated versions of origin and directions for SIMD operations
  v_originX = BroadcastFloat0(origin);
  v_originY = BroadcastFloat1(origin);
  v_originZ = BroadcastFloat2(origin);
  v_dirX = BroadcastFloat0(direction);
  v_dirY = BroadcastFloat1(direction);
  v_dirZ = BroadcastFloat2(direction);
  //record direction component signs
  //pull highest bit (sign bit) into low four bits in test and then invert those bits
  int test = move_mask(direction) ^ 0x0f; 
  assert(RAY_DIR_MINUS==0);
  assert(RAY_DIR_PLUS==1);
  dirType(KDST_SPLIT_X) = test&0x01;
  dirType(KDST_SPLIT_Y) = (test>>1)&0x01;
  dirType(KDST_SPLIT_Z) = (test>>2)&0x01;
  dirType(KDST_LEAF) = 0;
  /*
  dirType(KDST_SPLIT_X) = (dirX()<0)?RAY_DIR_MINUS:RAY_DIR_PLUS;
  dirType(KDST_SPLIT_Y) = (dirY()<0)?RAY_DIR_MINUS:RAY_DIR_PLUS;
  dirType(KDST_SPLIT_Z) = (dirZ()<0)?RAY_DIR_MINUS:RAY_DIR_PLUS;
  */
//  invDirection[3] = 0.0f;
}


void QuadRay::initFromRays(int numRays) {
    for(int i=0;i<numRays;i++) {
      quadOrigin[0][i] = ray[i].originX();
      quadOrigin[1][i] = ray[i].originY();
      quadOrigin[2][i] = ray[i].originZ();
      quadInvDir[0][i] = ray[i].invDirX();
      quadInvDir[1][i] = ray[i].invDirY();
      quadInvDir[2][i] = ray[i].invDirZ();
      quadTStart[i] = ray[i].tStart;
      quadTEnd[i] = ray[i].tEnd;
    }
    const int test = ray[0]._dirType_i32;
    for(int k=1;k<numRays;k++) {
      if (test != ray[k]._dirType_i32) {
        printf("Error rays do not match in direction signs\n");
        BRITE_DEBUGBREAK;
      }
    }
    //copy data from ray[0] to unused rays, but ensure that start > end so rays are never used
    float start = fabs(quadTStart[0]) + fabs(quadTEnd[0]) + 1.0f;
    for(int j=numRays;j<4;j++) {
      quadOrigin[0][j] = quadOrigin[0][0];
      quadOrigin[1][j] = quadOrigin[1][0];
      quadOrigin[2][j] = quadOrigin[2][0];
      quadInvDir[0][j] = quadInvDir[0][0];
      quadInvDir[1][j] = quadInvDir[1][0];
      quadInvDir[2][j] = quadInvDir[2][0];
      quadTStart[j] = start;
      quadTEnd[j] = quadTEnd[0];
    }
}
